package org.infodavid.common.model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;

import org.infodavid.common.persistence.IBasicDataObject;
import org.infodavid.common.persistence.IDataObject;
import org.infodavid.common.reflect.IClassNameFilter;
import org.infodavid.common.reflect.PackageUtil;

/**
 * The Class ModelFactoryTest.
 */
@SuppressWarnings({
    "unchecked", "rawtypes"
})
public class ModelFactoryTest {
	private static final List<String> UNIMPLEMENTED;

	static {
		UNIMPLEMENTED = new ArrayList<String>();

		UNIMPLEMENTED.add(IBasicDataObject.class.getName());
		UNIMPLEMENTED.add(IDataObject.class.getName());
		UNIMPLEMENTED.add(IEntity.class.getName());
		UNIMPLEMENTED.add(PropertyChangeEventFactory.class.getName());
		UNIMPLEMENTED.add(ICategory.class.getName());
		UNIMPLEMENTED.add(IEvent.class.getName());
		UNIMPLEMENTED.add(IBasicEvent.class.getName());
		UNIMPLEMENTED.add(IEmployment.class.getName());
	}

	/**
	 * The Class ClassFilter.
	 */
	public static class ClassFilter implements IClassNameFilter {

		/**
		 * See super class or interface. (non-Javadoc)
		 * @see com.org.infodavid.common.reflect.IClassNameFilter#accept(java.lang.String)
		 */
		@Override
		public boolean accept(final String name) {
			return !UNIMPLEMENTED.contains(name);
		}

	}

	/**
	 * The Interface InvalidType.
	 */
	public static interface InvalidType extends IBasicDataObject {}

	/** The factory. */
	private ModelFactory factory;

	/**
	 * Sets the up.
	 * @throws Exception the exception
	 */
	@Before
	public void setUp() throws Exception {
		factory = ModelFactory.getInstance();
	}

	/**
	 * Test create.
	 * @throws Exception the exception
	 */
	@Test
	public void testCreate() throws Exception {
		final Package pack = getClass().getPackage();
		final Set<Class> classes = PackageUtil.getInstance().getClasses(pack, new ClassFilter(), false);
		final Set<Class> errors = new HashSet<Class>();

		for (final Class type : classes) {
			if (IBasicDataObject.class.isAssignableFrom(type)) {
				try {
					factory.create(type);
				}
				catch (final Throwable e) {
					e.printStackTrace();
					errors.add(type);
				}
			}
		}

		Assert.assertTrue("Factory is not valid", errors.isEmpty());
	}

	/**
	 * Test get builder.
	 * @throws Exception the exception
	 */
	@Test
	public void testGetBuilder() throws Exception {
		final Package pack = getClass().getPackage();
		final Set<Class> classes = PackageUtil.getInstance().getClasses(pack, new ClassFilter(), false);
		final Set<Class> errors = new HashSet<Class>();

		for (final Class type : classes) {
			if (IBasicDataObject.class.isAssignableFrom(type)) {
				try {
					if (factory.getBuilder(type) == null) {
						errors.add(type);
					}
				}
				catch (final Throwable e) {
					e.printStackTrace();
					errors.add(type);
				}
			}
		}

		Assert.assertTrue("Factory is not valid", errors.isEmpty());
	}

	/**
	 * Test create using invalid.
	 * @throws Exception the exception
	 */
	@Test(expected = InstantiationException.class)
	public void testCreateUsingInvalid() throws Exception {
		factory.create(InvalidType.class);
	}

	/**
	 * Test create using null.
	 * @throws Exception the exception
	 */
	@Test(expected = InstantiationException.class)
	public void testCreateUsingNull() throws Exception {
		factory.create(null);
	}

	/**
	 * Test get builder using invalid.
	 * @throws Exception the exception
	 */
	@Test
	public void testGetBuilderUsingInvalid() throws Exception {
		Assert.assertNull("Not null", factory.getBuilder(InvalidType.class));
	}

	/**
	 * Test get builder using null.
	 * @throws Exception the exception
	 */
	@Test
	public void testGetBuilderUsingNull() throws Exception {
		Assert.assertNull("Not null", factory.getBuilder(null));
	}

	/**
	 * Test get instance.
	 */
	@Test
	public void testGetInstance() {
		Assert.assertEquals("Not a singleton", ModelFactory.getInstance(), ModelFactory.getInstance());
	}
}
